import fs from 'fs'
import fs_promises from 'fs/promises'
import type { MakeDirectoryOptions } from 'fs'

/**
 * 创建目录（如果不存在）
 */
export const mkdirIfNotExist = async (dirPath: string, options?: MakeDirectoryOptions) => {
  if (!fs.existsSync(dirPath)) {
    await fs_promises.mkdir(dirPath, options)
  }
}

export const mkdirIfNotExistSync = (dirPath: string, options?: MakeDirectoryOptions) => {
  if (!fs.existsSync(dirPath)) {
    fs.mkdirSync(dirPath, options)
  }
}

/**
 * 分块读取文件
 */
export function readFileWithChunks(options: ReadChunksOptions) {
  return new Promise<void>((resolve, reject) => {
    const { path: filePath, chunkSize = 4 * 1024, handler = () => {}, end = () => {} } = options

    const total = fs.statSync(filePath).size
    let current = 0

    const fileStream = fs.createReadStream(filePath, { highWaterMark: chunkSize })

    fileStream.on('data', (chunk: Buffer) => {
      current += chunk.length
      handler(chunk, total, current)
    })

    fileStream.on('end', () => {
      end()
      resolve()
    })

    fileStream.on('error', reject)
  })
}

type ReadChunksOptions = {
  path: string
  chunkSize?: number
  handler?: (chunk: Buffer, total: number, current: number) => void
  end?: () => void
}

/**
 * 读取文件某一块的数据（包含结束字节）
 */
export function readChunk(filePath: string, start: number, end: number) {
  return new Promise<Buffer>((resolve, reject) => {
    const readStream = fs.createReadStream(filePath, { start, end })
    const chunks: Buffer[] = []
    readStream.on('data', (chunk) => chunks.push(chunk as Buffer))
    readStream.on('end', () => resolve(Buffer.concat(chunks)))
    readStream.on('error', reject)
  })
}

/**
 * 创建分块读取迭代器
 */
export function createChunkReadIterator(filePath: string, chunkSize: number) {
  if (!fs.existsSync(filePath)) {
    throw new Error(`File ${filePath} does not exist.`)
  }

  const stats = fs.statSync(filePath)

  if (!stats.isFile()) {
    throw new Error(`${filePath} is not a file.`)
  }

  const size = stats.size
  let index = 0

  return {
    totalChunks: Math.ceil(stats.size / chunkSize),
    size,
    chunkSize,
    [Symbol.iterator]() {
      return {
        next() {
          const start = index
          const done = index >= size
          let end = index + chunkSize

          if (end > size) end = size

          index += chunkSize

          return {
            done,
            value: done ? Promise.resolve(Buffer.from([])) : readChunk(filePath, start, end - 1)
          }
        }
      }
    }
  }
}

/**
 * 写入一个空文件
 */
export const writeEmptyFile = (filePath: string) => fs_promises.writeFile(filePath, Buffer.from([]))

export const getFileSize = async (filePath: string) => {
  const stats = await fs_promises.stat(filePath)
  return stats.size
}
